import type { Component, ComponentOptionsMixin } from 'vue'
import { useDebounceFn } from '@vueuse/core'
import CryptoJS from 'crypto-js'

/**
 * 组件名称代理
 * @see 注意KeepAlive组件需要等待缓存组件失活后才能销毁
 * @param name 组件名称
 */
export const useProxyComponentName = () => {
  const map = new Map<string, Component>()
  return (name: string) => {
    if (map.has(name)) {
      return map.get(name)
    }
    const proxyComponent = defineComponent(
      (_, { slots }) => {
        onUnmounted(() => map.delete(name))
        const slot = slots.default?.()[0]
        if (slot?.type) {
          return () =>
            Object.assign(h(slot.type as Component, slot.props, slot.children || []), {
              dirs: slot.dirs,
            })
        }
        return () => null
      },
      { name },
    )
    map.set(name, proxyComponent)
    return proxyComponent
  }
}

/**
 * 组件放置列表
 * @description 当前渲染的指令式组件列表
 */
export const placeComponents_list = shallowReactive<
  {
    component: Component
    tsxProps?: ComponentOptionsMixin
  }[]
>([])

/**
 * 组件放置实现
 * @description 用于放置指令式组件的渲染位置，通常放置在App.vue的最后
 */
export const PlaceComponents = defineComponent(() => () => {
  return placeComponents_list.map((itme) => {
    return h(itme.component, {
      onPlaceUnmount: () =>
        setTimeout(() => placeComponents_list.splice(placeComponents_list.indexOf(itme), 1)),
      ...(itme.tsxProps || {}),
    })
  })
})

/**
 * 防抖函数-立即执行
 * @param fn 需要防抖的函数
 * @param delay 防抖时间
 * @returns 防抖后的函数
 */
export const useDebounceFnImmediate = <F extends (...args: any[]) => void>(
  fn: F,
  delay: number,
) => {
  let first = true
  const _fn = useDebounceFn((...args) => {
    first = true
    fn(...args)
  }, delay)
  return ((...args) => {
    if (first) {
      first = false
      fn(...args)
    } else {
      _fn(...args)
    }
  }) as F
}

/**
 * AES加密
 * @param text 需要加密的文本
 * @returns 加密后的文本
 */
export const aesEncrypt = (text: string) =>
  CryptoJS.AES.encrypt(text, CryptoJS.enc.Utf8.parse(__ENV__.VITE_AES_KEY), {
    iv: CryptoJS.enc.Utf8.parse(__ENV__.VITE_AES_IV),
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7,
  }).toString()
